Imports System.DirectoryServices

Namespace DirectoryServices

  ''' <summary>
  ''' User's Active Directory Info
  ''' </summary>
  ''' <remarks></remarks>
  Public Structure UserInfo
    Private _contactName As String
    ''' <summary>
    ''' Account Contact Name
    ''' </summary>
    ''' <value></value>
    ''' <returns>String</returns>
    ''' <remarks></remarks>
    Public Property ContactName() As String
      Get
        Return _contactName
      End Get
      Set(ByVal value As String)
        _contactName = value
      End Set
    End Property
    Private _email As String
    ''' <summary>
    ''' Account Email Address
    ''' </summary>
    ''' <value></value>
    ''' <returns>String</returns>
    ''' <remarks></remarks>
    Public Property Email() As String
      Get
        Return _email
      End Get
      Set(ByVal value As String)
        _email = value
      End Set
    End Property
    Private _department As String
    ''' <summary>
    ''' Account Department Name
    ''' </summary>
    ''' <value></value>
    ''' <returns>String</returns>
    ''' <remarks></remarks>
    Public Property Department() As String
      Get
        Return _department
      End Get
      Set(ByVal value As String)
        _department = value
      End Set
    End Property
    Private _manager As String
    ''' <summary>
    ''' Account Managers' Name
    ''' </summary>
    ''' <value></value>
    ''' <returns>String</returns>
    ''' <remarks></remarks>
    Public Property Manager() As String
      Get
        Return _manager
      End Get
      Set(ByVal value As String)
        _manager = value
      End Set
    End Property
    Private _telephone As String
    ''' <summary>
    ''' Contacts telephone number.
    ''' </summary>
    ''' <value>String</value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property Telephone() As String
      Get
        Return _telephone
      End Get
      Set(ByVal value As String)
        _telephone = value
      End Set
    End Property
    Private _title As String
    ''' <summary>
    ''' Contacts Title
    ''' </summary>
    ''' <value></value>
    ''' <returns>String</returns>
    ''' <remarks></remarks>
    Public Property Title() As String
      Get
        Return _title
      End Get
      Set(ByVal value As String)
        _title = value
      End Set
    End Property
    Private _fax As String
    ''' <summary>
    ''' Users fax number.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property Fax() As String
      Get
        Return _fax
      End Get
      Set(ByVal value As String)
        _fax = value
      End Set
    End Property

    Private _firstName As String
    ''' <summary>
    ''' Users first name
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property FirstName() As String
      Get
        Return _firstName
      End Get
      Set(ByVal value As String)
        _firstName = value
      End Set
    End Property
    Private _lastName As String
    ''' <summary>
    ''' Users last name.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property LastName() As String
      Get
        Return _lastName
      End Get
      Set(ByVal value As String)
        _lastName = value
      End Set
    End Property
    Private _deliveryOffice As String
    ''' <summary>
    ''' Users delivery office
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property DeliveryOffice() As String
      Get
        Return _deliveryOffice
      End Get
      Set(ByVal value As String)
        _deliveryOffice = value
      End Set
    End Property


    ''' <summary>
    ''' Returns the fully qualified type name of this instance.
    ''' </summary>
    ''' <returns>
    ''' A <see cref="T:System.String" /> containing a fully qualified type name.
    ''' </returns>
    Public Overrides Function ToString() As String
      Return Me.ContactName
    End Function


    ''' <summary>
    ''' Returns the hash code for this instance.
    ''' </summary>
    ''' <returns>
    ''' A 32-bit signed integer that is the hash code for this instance.
    ''' </returns>
    Public Overloads Overrides Function GetHashCode() As Integer
      Return Me.ContactName.GetHashCode Or Me.DeliveryOffice.GetHashCode Or Me.Department.GetHashCode Or Me.Email.GetHashCode Or Me.Fax.GetHashCode Or Me.FirstName.GetHashCode Or Me.LastName.GetHashCode Or Me.Manager.GetHashCode Or Me.Telephone.GetHashCode Or Me.Title.GetHashCode
    End Function

    ''' <summary>
    ''' Indicates whether this instance and a specified object are equal.
    ''' </summary>
    ''' <param name="obj">Another object to compare to.</param>
    ''' <returns>
    ''' true if <paramref name="obj" /> and this instance are the same type and represent the same value; otherwise, false.
    ''' </returns>
    Public Overloads Overrides Function Equals(ByVal obj As [Object]) As Boolean
      Dim testObject As UserInfo = CType(obj, UserInfo)

      If Me.ContactName = testObject.ContactName AndAlso Me.DeliveryOffice = testObject.DeliveryOffice AndAlso Me.Department = testObject.Department AndAlso Me.Email = testObject.Email AndAlso Me.Fax = testObject.Fax AndAlso Me.FirstName = testObject.FirstName AndAlso Me.LastName = testObject.LastName AndAlso Me.Manager = testObject.Manager AndAlso Me.Telephone = testObject.Telephone AndAlso Me.Title = testObject.Title Then
        Return True
      Else
        Return False
      End If

    End Function
  End Structure



  ''' <summary>
  ''' Active Directory Helper Class
  ''' </summary>
  ''' <remarks></remarks>
  Public NotInheritable Class ActiveDirectoryHelper
    Private Const STR_AccountFilter As String = "(SAMAccountName={0})"
    Private Const STR_ContactDepartment As String = "department"
    Private Const STR_ContactEmail As String = "mail"
    Private Const STR_ContactManager As String = "manager"
    Private Const STR_ContactName As String = "cn"
    Private Const STR_ContactNameFilter As String = "(CN={0})"
    Private Const STR_LDAPPrefix As String = "LDAP://"
    Private Const STR_Member As String = "member"
    Private Const STR_SAMAccountName As String = "SAMAccountName"
    Private Const STR_ContactTelephone As String = "telephoneNumber"
    Private Const STR_ContactTitle As String = "title"
    Private Const STR_ContactFirstName As String = "givenName"
    Private Const STR_ContactLastName As String = "sn"
    Private Const STR_ContactFax As String = "facsimileTelephoneNumber"
    Private Const STR_ContactDeliveryOffice As String = "physicalDeliveryOfficeName"


    ''' <summary>
    ''' Retrieves the users Active Directory Contact Name.
    ''' </summary>
    ''' <param name="accountName">Account Name</param>
    ''' <returns>String</returns>
    ''' <remarks></remarks>
    Public Shared Function UserContactName(ByVal accountName As String) As String
      Dim contactName As String = String.Empty

      contactName = UserInfo(accountName).ContactName

      Return contactName

    End Function

    ''' <summary>
    ''' Retrieves the users Active Directory E-Mail address
    ''' </summary>
    ''' <param name="accountName">Account Name</param>
    ''' <returns>String</returns>
    ''' <remarks></remarks>
    Public Shared Function UserEmail(ByVal accountName As String) As String
      Dim emailAddress As String = String.Empty

      emailAddress = UserInfo(accountName).Email

      Return emailAddress

    End Function

    ''' <summary>
    ''' Retrieves the users Active Directory information.
    ''' </summary>
    ''' <param name="userLogin">Account Name</param>
    ''' <returns>UserInfo</returns>
    ''' <remarks></remarks>
    Public Shared Function UserInfo(ByVal userLogin As String) As UserInfo


      Dim info As New UserInfo

      Using searcher As New System.DirectoryServices.DirectorySearcher

        SetSearcherDomain(userLogin, searcher)

        Dim account As String = String.Empty

        SplitLogin(userLogin, Nothing, account)

        searcher.Filter = String.Format(CultureInfo.InvariantCulture, STR_AccountFilter, account)
        searcher.PropertiesToLoad.Add(STR_ContactName)
        searcher.PropertiesToLoad.Add(STR_ContactEmail)
        searcher.PropertiesToLoad.Add(STR_ContactDepartment)
        searcher.PropertiesToLoad.Add(STR_ContactManager)
        searcher.PropertiesToLoad.Add(STR_ContactTelephone)
        searcher.PropertiesToLoad.Add(STR_ContactTitle)
        searcher.PropertiesToLoad.Add(STR_ContactFirstName)
        searcher.PropertiesToLoad.Add(STR_ContactLastName)
        searcher.PropertiesToLoad.Add(STR_ContactFax)
        searcher.PropertiesToLoad.Add(STR_ContactDeliveryOffice)

        Dim result As System.DirectoryServices.SearchResult = searcher.FindOne

        If Not IsNothing(result) Then

          info.ContactName = ExtractADPropertyValue(STR_ContactName, result)
          info.Email = ExtractADPropertyValue(STR_ContactEmail, result)
          info.Department = ExtractADPropertyValue(STR_ContactDepartment, result)
          info.Manager = GetManagerName(ExtractADPropertyValue(STR_ContactManager, result))
          info.Telephone = ExtractADPropertyValue(STR_ContactTelephone, result)
          info.Title = ExtractADPropertyValue(STR_ContactTitle, result)
          info.FirstName = ExtractADPropertyValue(STR_ContactFirstName, result)
          info.LastName = ExtractADPropertyValue(STR_ContactLastName, result)
          info.Fax = ExtractADPropertyValue(STR_ContactFax, result)
          info.DeliveryOffice = ExtractADPropertyValue(STR_ContactDeliveryOffice, result)

        End If

      End Using

      Return info

    End Function

    Public Shared Function LoadAllUsers() As System.Collections.ObjectModel.ReadOnlyCollection(Of UserInfo)
      Return LoadAllUsers(Nothing)
    End Function

    ''' <summary>
    ''' Loads all the users in the current domain.
    ''' </summary>
    ''' <returns>List of <see cref="System.Collections.ObjectModel.ReadOnlyCollection(Of UserInfo)"></see></returns>
    ''' <remarks></remarks>
    Public Shared Function LoadAllUsers(ByVal domain As String) As System.Collections.ObjectModel.ReadOnlyCollection(Of UserInfo)

      Dim users As New System.Collections.Generic.List(Of UserInfo)

      Using searcher As New System.DirectoryServices.DirectorySearcher

        'Set Domain if avaialable
        If Not String.IsNullOrEmpty(domain) Then
          searcher.SearchRoot = New DirectoryEntry(String.Format("{0}{1}", STR_LDAPPrefix, domain))
        End If

        searcher.Filter = "(&(objectClass=Person)(objectClass=User)(!objectClass=computer))"
        searcher.Sort.Direction = System.DirectoryServices.SortDirection.Ascending
        searcher.Sort.PropertyName = STR_ContactName
        'Load Properties that we want
        searcher.PropertiesToLoad.Add(STR_ContactName)
        searcher.PropertiesToLoad.Add(STR_ContactEmail)
        searcher.PropertiesToLoad.Add(STR_ContactDepartment)
        searcher.PropertiesToLoad.Add(STR_ContactManager)
        searcher.PropertiesToLoad.Add(STR_ContactTelephone)
        searcher.PropertiesToLoad.Add(STR_ContactTitle)
        searcher.PropertiesToLoad.Add(STR_ContactFirstName)
        searcher.PropertiesToLoad.Add(STR_ContactLastName)
        searcher.PropertiesToLoad.Add(STR_ContactFax)
        searcher.PropertiesToLoad.Add(STR_ContactDeliveryOffice)

        Dim results As System.DirectoryServices.SearchResultCollection = searcher.FindAll

        If Not IsNothing(results) AndAlso results.Count > 0 Then

          For Each result As System.DirectoryServices.SearchResult In results
            Dim info As New UserInfo

            info.ContactName = ExtractADPropertyValue(STR_ContactName, result)
            info.Email = ExtractADPropertyValue(STR_ContactEmail, result)
            info.Department = ExtractADPropertyValue(STR_ContactDepartment, result)
            info.Manager = GetManagerName(ExtractADPropertyValue(STR_ContactManager, result))
            info.Telephone = ExtractADPropertyValue(STR_ContactTelephone, result)
            info.Title = ExtractADPropertyValue(STR_ContactTitle, result)
            info.FirstName = ExtractADPropertyValue(STR_ContactFirstName, result)
            info.LastName = ExtractADPropertyValue(STR_ContactLastName, result)
            info.Fax = ExtractADPropertyValue(STR_ContactFax, result)
            info.DeliveryOffice = ExtractADPropertyValue(STR_ContactDeliveryOffice, result)

            users.Add(info)

          Next

        End If

      End Using

      Return New System.Collections.ObjectModel.ReadOnlyCollection(Of UserInfo)(users)

    End Function

    ''' <summary>
    ''' Validates an Active Directory Account Name
    ''' </summary>
    ''' <param name="userLogin">Account Name</param>
    ''' <returns>True if Account Name is active in the current domain.</returns>
    ''' <remarks></remarks>
    Public Shared Function ValidateAccountName(ByVal userLogin As String) As Boolean



      Dim valid As Boolean = False

      Using searcher As New System.DirectoryServices.DirectorySearcher

        SetSearcherDomain(userLogin, searcher)

        Dim account As String = String.Empty

        SplitLogin(userLogin, Nothing, account)

        searcher.Filter = String.Format(CultureInfo.InvariantCulture, STR_AccountFilter, account)

        Dim result As System.DirectoryServices.SearchResult = searcher.FindOne

        If Not IsNothing(result) Then
          valid = True
        End If

      End Using

      Return valid

    End Function

    ''' <summary>
    ''' Verifies the current user in belongs to the given Active Directory group
    ''' </summary>
    ''' <param name="group">Group name</param>
    ''' <returns>True if in group</returns>
    ''' <remarks></remarks>
    Public Overloads Shared Function IsUserInGroup(ByVal group As String) As Boolean

      If ApplicationHelper.IsAspNet Then
        Return System.Web.HttpContext.Current.User.IsInRole(group)
      Else
        Return My.User.IsInRole(group)
      End If

    End Function

    ''' <summary>
    ''' Verifies the Active Directory Account Name belongs to one of the given groups.
    ''' </summary>
    ''' <param name="userLogin">Account Name</param>
    ''' <param name="groups">Group(s)</param>
    ''' <returns>True if the Account Name belongs to at least one group.</returns>
    ''' <remarks></remarks>
    Public Overloads Shared Function IsUserInGroup(ByVal userLogin As String, ByVal ParamArray groups As String()) As Boolean

      If String.IsNullOrEmpty(userLogin) Then
        Throw New ArgumentNullException("logIn")
      End If

      If Not Collections.ArrayHelper.IsValidArray(groups) Then
        Throw New ArgumentNullException("groups")
      End If

      Using searcher As System.DirectoryServices.DirectorySearcher = New System.DirectoryServices.DirectorySearcher()

        SetSearcherDomain(userLogin, searcher)

        For Each groupName As String In groups

          searcher.Filter = String.Format(CultureInfo.CurrentCulture, STR_ContactNameFilter, groupName)

          Dim account As String = String.Empty
          SplitLogin(userLogin, Nothing, account)

          Using results As System.DirectoryServices.SearchResultCollection = searcher.FindAll()

            For Each result As System.DirectoryServices.SearchResult In results

              Dim resultProperties As System.DirectoryServices.ResultPropertyCollection = result.Properties

              For Each members As Object In resultProperties(STR_Member)

                Using memberEntry As System.DirectoryServices.DirectoryEntry = New System.DirectoryServices.DirectoryEntry(STR_LDAPPrefix + members.ToString)

                  'Validate if user in the group
                  If account = ExtractADPropertyValue(STR_SAMAccountName, memberEntry) Then
                    Return True
                  End If

                End Using

              Next

            Next

          End Using

        Next

      End Using

      Return False

    End Function

    ''' <summary>
    ''' Verifies the Active Directory Account Name belongs to the given groups.
    ''' </summary>
    ''' <param name="accountName">Account Name</param>
    ''' <param name="groupName">Group name</param>
    ''' <returns>True if the Account Name belongs to the group</returns>
    ''' <remarks></remarks>
    Public Overloads Shared Function IsUserInGroup(ByVal accountName As String, ByVal groupName As String) As Boolean
      Return IsUserInGroup(accountName, New String() {groupName})
    End Function

    Public Shared Sub SplitLogin(ByRef logIn As String, ByRef domain As String, ByRef accountName As String)
      logIn = logIn.Replace(Constants.CharacterForwardSlash, Constants.CharacterBackslash)

      If logIn.Contains(Constants.CharacterBackslash) Then
        With logIn.Split(Constants.CharacterBackslash)
          If Not domain Is Nothing Then
            domain = .GetValue(0).ToString
          End If
          If Not accountName Is Nothing Then
            accountName = .GetValue(1).ToString
          End If
        End With
      Else
        accountName = logIn
      End If
    End Sub

    Public Shared Function ExtractADPropertyValue(ByVal propertyName As String, ByVal result As System.DirectoryServices.SearchResult) As String

      Dim propertyValue As String = String.Empty

      If result.Properties.Contains(propertyName) Then
        propertyValue = result.Properties(propertyName).Item(0).ToString()
      End If

      Return propertyValue

    End Function

    Public Shared Function ExtractADPropertyValue(ByVal propertyName As String, ByVal entry As System.DirectoryServices.DirectoryEntry) As String

      Dim propertyValue As String = String.Empty

      If entry.Properties.Contains(propertyName) Then
        propertyValue = entry.Properties(propertyName).Item(0).ToString()
      End If

      Return propertyValue

    End Function

    Private Shared Function GetManagerName(ByVal name As String) As String

      Dim managerName As String = String.Empty

      If Not String.IsNullOrEmpty(name) Then

        If name.Split(Constants.CharacterComma).Length > 0 Then
          managerName = name.Split(Constants.CharacterComma)(0).Remove(0, 3)
        End If
      End If

      Return managerName

    End Function

    Private Shared Sub SetSearcherDomain(ByVal userLogin As String, ByVal searcher As System.DirectoryServices.DirectorySearcher)
      Dim domain As String = String.Empty

      SplitLogin(userLogin, domain, Nothing)

      'Set Domain if avaialable
      If Not String.IsNullOrEmpty(domain) Then
        searcher.SearchRoot = New DirectoryEntry(String.Format("{0}{1}", STR_LDAPPrefix, domain))
      End If

    End Sub
    Private Sub New()
    End Sub

  End Class
End Namespace